package luke.media.video;

import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.javacv.*;

import java.awt.*;

import java.awt.geom.Ellipse2D;
import java.awt.RadialGradientPaint;
import java.awt.image.BufferedImage;

import java.util.List;
import java.util.Random;

import luke.system.callback.ProgressCallback;


/**
 * 圆环式频谱视频生成器类，用于生成带有播放列表和圆环式频谱能量线动画的视频。
 * 视频渲染器只负责根据频带能量画图、画播放列表和抬头时间，生成没有音频的视频
 */
public class CircularSpectrumVideoGenerator extends BaseSpectrumVideoGenerator {
    // 使用统一的星星粒子系统
    private List<VideoUIUtils.StarParticle> starParticles = new java.util.ArrayList<>();
    private Random random;

    /**
     * 构造函数 - 简化版本，只保留必要参数
     * 
     * @param outputVideoPath 输出视频路径
     * @param width 视频宽度
     * @param height 视频高度
     * @param playlistContent 播放列表内容
     * @param videoTitle 视频标题
     * @param fps 视频帧率（由调度器传入）
     * @param requiredBarCount 所需频段数量（由调度器传入）
     */
    public CircularSpectrumVideoGenerator(String outputVideoPath, 
                                         int width, 
                                         int height, 
                                         List<String> playlistContent, 
                                         String videoTitle,
                                         double fps,
                                         int requiredBarCount) {
        super(outputVideoPath, width, height, playlistContent, videoTitle, fps, requiredBarCount);
        
        // 初始化随机数生成器
        this.random = new Random();
    }

    @Override
    public int getRequiredBarCount() {
        return super.getRequiredBarCount();
    }
    
    /**
     * 生成带有播放列表和圆环式频谱能量线动画的视频
     * @param durationMs 视频总时长（毫秒）
     * @param progressCallback 进度回调
     * @return 生成成功返回true，失败返回false
     */
    public boolean generateVideoWithCircularSpectrum(double durationMs, ProgressCallback progressCallback) {
        FFmpegFrameRecorder recorder = null;

        try {
            // 设置视频参数（帧率已由调度器传入）
            double durationInSeconds = durationMs / 1000.0;
            this.totalFrames = (int) (durationInSeconds * fps);

            System.out.println("总时长: " + durationInSeconds + "秒, 总帧数: " + totalFrames);

            // 创建视频录制器
            recorder = new FFmpegFrameRecorder(outputVideoPath, width, height);
            // 使用无损格式提高合并速度
            if (outputVideoPath.endsWith("_segment_0.mp4") || outputVideoPath.contains("_segment_")) {
                // 对于中间片段，使用无损格式
                recorder.setVideoCodec(avcodec.AV_CODEC_ID_FFV1); // 使用FFV1无损编解码器
                recorder.setFormat("matroska"); // 使用MKV容器格式
            } else {
                // 对于最终输出，使用标准MP4格式
                recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
                recorder.setFormat("mp4");
            }
            recorder.setFrameRate(fps); // 使用调度器传入的帧率
            recorder.setPixelFormat(0);
            recorder.setVideoBitrate(2000000); // 提高比特率以获得更好的视频质量

            // 启动录制器
            recorder.start();

            // 生成视频帧
            for (long frameIndex = 0; frameIndex < totalFrames; frameIndex++) {
                Java2DFrameConverter converter = null;
                BufferedImage frameImage = null;
                org.bytedeco.javacv.Frame frame = null;

                try {
                    // 计算当前时间点
                    double currentTime = frameIndex / fps;

                    // 创建视频帧
                    frameImage = createVideoFrame((int) frameIndex);

                    // 创建帧转换器
                    converter = new Java2DFrameConverter();

                    // 转换为Frame并记录
                    frame = converter.convert(frameImage);
                    recorder.record(frame);
                } finally {
                    // 确保所有资源都被正确释放
                    try {
                        if (frame != null) {
                            frame.close();
                        }
                        if (frameImage != null) {
                            frameImage.flush();
                        }
                        if (converter != null) {
                            converter.close();
                        }
                    } catch (Exception e) {
                        System.err.println("释放帧资源时出错: " + e.getMessage());
                    }
                }

                // 更新进度
                if (frameIndex % 30 == 0) { // 每30帧更新一次进度
                    double progress = (double) frameIndex / totalFrames * 100;
                    System.out.printf("进度: %.1f%%, 已处理帧数: %d/%d%n", progress, frameIndex, totalFrames);

                    if (progressCallback != null) {
                        progressCallback.onProgress("正在生成视频帧 " + (frameIndex+1) + "/" + totalFrames, progress);
                    }

                    // 定期进行垃圾回收，减少内存占用
                    if (frameIndex % 150 == 0) {
                        System.gc();
                    }
                }
            }

            System.out.println("视频生成成功: " + outputVideoPath);
            if (progressCallback != null) {
                progressCallback.onProgress("视频生成完成", 100);
            }
            return true;

        } catch (Exception e) {
            System.err.println("视频生成失败: " + e.getMessage());
            e.printStackTrace();
            return false;
        } finally {
            // 确保所有资源都被正确释放
            try {
                if (recorder != null) {
                    recorder.stop();
                    recorder.release();
                }
            } catch (Exception e) {
                System.err.println("释放录制器资源时出错: " + e.getMessage());
            }

            // 强制进行垃圾回收
            System.gc();
        }
    }
    
    @Override
    public boolean generateVideo(long durationMs, ProgressCallback progressCallback) {
        return generateVideoWithCircularSpectrum(durationMs, progressCallback);
    }

    /**
     * 创建视频帧，包含播放列表和圆环式频谱动画
     * @param frameIndex 帧索引
     * @return 视频帧图像
     */
    private BufferedImage createVideoFrame(int frameIndex) {
        // 创建图像，使用TYPE_3BYTE_BGR以避免颜色异常
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
        Graphics2D g2d = null;

        try {
            g2d = image.createGraphics();

            // 启用抗锯齿
            g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
            g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

            // 绘制星空背景
            VideoUIUtils.drawStarfieldBackground(g2d, width, height, frameIndex);

            // 绘制圆环频谱动画
            drawCircularSpectrumAnimation(g2d, frameIndex);

            // 绘制播放列表
            drawPlaylist(g2d);

            // 更新和绘制星星粒子
            VideoUIUtils.updateAndDrawStarParticles(g2d, starParticles, width, height, frameIndex, random);
        } finally {
            // 确保Graphics2D资源被释放
            if (g2d != null) {
                g2d.dispose();
            }
        }

        return image;
    }

    /**
     * 绘制背景渐变效果
     * @param g2d Graphics2D对象
     */
    private void drawBackgroundGradient(Graphics2D g2d) {
        // 由于使用了统一的星空背景，此处不再需要额外的渐变背景
        // 保留方法以确保兼容性，但不执行任何操作
    }

    /**
     * 绘制圆环式频谱动画
     * @param g2d Graphics2D对象
     * @param frameIndex 帧索引
     */
    private void drawCircularSpectrumAnimation(Graphics2D g2d, int frameIndex) {
        // 计算圆心坐标
        int centerX = width / 2;
        int centerY = height / 2;

        // 计算半径（取宽高中较小值的一半，再减去边距）
        int radius = Math.min(width, height) / 2 - 100;

        // 生成当前时间点的频谱数据
        float[] spectrumData = generateCurrentSpectrumData(frameIndex, 128);

        // 计算平均能量值，用于背景亮度变化
        float averageEnergy = calculateAverageEnergy(spectrumData);

        // 打乱频带顺序以改善低音和高音交接处的断崖问题
        float[] shuffledSpectrumData = shuffleSpectrumData(spectrumData);

        // 绘制背景照亮效果（根据总能量变化）
        drawBackgroundIllumination(g2d, centerX, centerY, radius, averageEnergy);

        // 创建一个路径来绘制频谱线
        java.awt.geom.Path2D.Float path = new java.awt.geom.Path2D.Float();

        // 计算每个频带的角度
        double anglePerBand = 360.0 / 128;

        // 绘制频谱能量线（连接所有能量最高点形成一条线）
        boolean firstPoint = true;
        for (int i = 0; i < 128; i++) {
            // 计算频带的角度（从顶部开始，顺时针）
            double angle = i * anglePerBand - 90; // -90度使0度在顶部
            double angleRad = Math.toRadians(angle);

            // 计算频带的半径（基于打乱后的频谱数据，调整内部圆环和边界）
            double bandRadius = radius * (0.6 + 0.4 * shuffledSpectrumData[i]); // 半径在60%-100%之间变化，增大内部圆环

            // 计算频带的点坐标
            double x = centerX + bandRadius * Math.cos(angleRad);
            double y = centerY + bandRadius * Math.sin(angleRad);

            // 添加点到路径
            if (firstPoint) {
                path.moveTo(x, y);
                firstPoint = false;
            } else {
                path.lineTo(x, y);
            }
        }

        // 闭合路径
        path.closePath();

        // 绘制发光效果
        drawGlowEffect(g2d, path, averageEnergy);

        // 绘制频谱线主体
        g2d.setStroke(new BasicStroke(3, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
        g2d.setColor(new Color(180, 120, 255)); // 紫色
        g2d.draw(path);
    }

    /**
     * 计算平均能量值
     * @param spectrumData 频谱数据
     * @return 平均能量值
     */
    private float calculateAverageEnergy(float[] spectrumData) {
        if (spectrumData == null || spectrumData.length == 0) {
            return 0;
        }

        float sum = 0;
        for (float value : spectrumData) {
            sum += value;
        }
        return sum / spectrumData.length;
    }

    /**
     * 绘制背景照亮效果
     * @param g2d Graphics2D对象
     * @param centerX 圆心X坐标
     * @param centerY 圆心Y坐标
     * @param radius 半径
     * @param energy 能量值，用于调整亮度
     */
    private void drawBackgroundIllumination(Graphics2D g2d, int centerX, int centerY, int radius, float energy) {
        // 根据能量值调整亮度
        float energyRatio = Math.min(1.0f, energy * 3); // 放大能量值以获得更明显的亮度变化

        // 创建径向渐变画笔，用于绘制背景照亮效果
        float[] fractions = {0.0f, 0.7f, 1.0f};
        Color[] colors = {
            new Color(100, 60, 180, (int) (80 * energyRatio)), // 内圈紫色光晕
            new Color(60, 30, 120, (int) (40 * energyRatio)),  // 中圈深紫色光晕
            new Color(20, 10, 60, 0)                            // 外圈透明
        };

        RadialGradientPaint gradient = new RadialGradientPaint(
            centerX, centerY, radius * 1.5f, fractions, colors);
        g2d.setPaint(gradient);
        g2d.fill(new Ellipse2D.Float(
            centerX - radius * 1.5f, centerY - radius * 1.5f,
            radius * 3, radius * 3));
    }

    /**
     * 绘制光晕效果
     * @param g2d Graphics2D对象
     * @param path 频谱线路径
     * @param energy 能量值，用于调整发光效果
     */
    private void drawGlowEffect(Graphics2D g2d, java.awt.geom.Path2D.Float path, float energy) {
        // 保存原始复合模式
        Composite originalComposite = g2d.getComposite();

        // 根据能量值调整发光强度
        float energyFactor = 0.3f + energy * 0.7f;

        // 绘制多层模糊光晕效果，每层比前一层更粗
        float[] strokeWidths = {6, 10, 16, 24, 34, 46}; // 逐渐变粗的线宽
        float[] alphas = {0.3f, 0.2f, 0.15f, 0.1f, 0.07f, 0.05f}; // 逐渐变透明

        for (int i = 0; i < strokeWidths.length; i++) {
            float alpha = alphas[i] * energyFactor;
            g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha));
            g2d.setStroke(new BasicStroke(strokeWidths[i], BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));

            // 发光颜色（紫色发光）
            g2d.setColor(new Color(180, 120, 255));
            g2d.draw(path);
        }

        // 重置复合模式
        g2d.setComposite(originalComposite);
    }

    /**
     * 打乱频谱数据以改善低音和高音交接处的断崖问题
     * @param spectrumData 原始频谱数据
     * @return 打乱后的频谱数据
     */
    private float[] shuffleSpectrumData(float[] spectrumData) {
        int length = spectrumData.length;
        float[] shuffledData = new float[length];

        // 复制原始数据
        System.arraycopy(spectrumData, 0, shuffledData, 0, length);

        // 对整个频带进行随机打乱
        shuffleArray(shuffledData, 0, length);

        return shuffledData;
    }

    /**
     * 对数组指定范围内的元素进行随机打乱
     * @param array 数组
     * @param start 起始索引（包含）
     * @param end 结束索引（不包含）
     */
    private void shuffleArray(float[] array, int start, int end) {
        Random random = new Random();
        for (int i = end - 1; i > start; i--) {
            int index = start + random.nextInt(i - start + 1);
            float temp = array[index];
            array[index] = array[i];
            array[i] = temp;
        }
    }

    /**
     * 生成当前帧的频谱数据
     * @param frameIndex 帧索引
     * @param bandCount 频带数量
     * @return 频谱数据数组
     */
    private float[] generateCurrentSpectrumData(int frameIndex, int bandCount) {
        // 如果有预计算的频谱数据，使用预计算的数据
        if (precomputedSpectrums != null && precomputedSpectrums.length > 0) {
            // 计算当前帧在预计算频谱数据中的索引
            int spectrumIndex = Math.min(frameIndex, precomputedSpectrums.length - 1);
            
            // 将频谱数据转换为频带数据
            float[] spectrumData = new float[bandCount];
            double[] currentSpectrum = precomputedSpectrums[spectrumIndex];
            int spectrumLength = currentSpectrum.length;

            // 计算频谱数据的最大值，用于归一化
            double maxSpectrumValue = 0;
            for (int i = 0; i < spectrumLength; i++) {
                if (currentSpectrum[i] > maxSpectrumValue) {
                    maxSpectrumValue = currentSpectrum[i];
                }
            }

            // 避免除零错误
            if (maxSpectrumValue == 0) {
                maxSpectrumValue = 1;
            }

            // 将频谱数据映射到频带并增强视觉效果
            for (int i = 0; i < bandCount; i++) {
                // 计算在频谱数据中的索引
                int index = (int) ((double) i / bandCount * spectrumLength);
                index = Math.max(0, Math.min(index, spectrumLength - 1));

                // 获取频谱值并归一化
                double value = currentSpectrum[index];

                // 使用更柔和的归一化方式，提高小值可见性
                double normalizedValue = value / maxSpectrumValue;

                // 应用更强的非线性增强函数（五次方根）以提高小值的可见性
                value = Math.pow(normalizedValue, 1.0/3.0);

                // 进一步放大低频部分以增强视觉效果
                if (i < bandCount / 4) {
                    value *= 1.3; // 更大幅度增强低频部分
                } else if (i < bandCount * 3 / 4) {
                    value *= 1.6; // 更大幅度增强中频部分
                } else {
                    value *= 1.6; // 更大幅度增强高频部分
                }

                // 应用对数增强使频带更加猛烈，增强小值可见性
                if (value > 0) {
                    value = Math.log10(1 + 5 * value); // 增强对数参数以提高小值可见性
                }

                // 适度放大整体值以使频带更长，但避免过度放大导致截断
                value *= 1.4; // 增加整体放大倍数

                // 确保有一个最小值，使小能量也能显示
                value = Math.max(value, 0.0012);

                // 确保值在0-1范围内
                spectrumData[i] = (float) Math.min(1.0, Math.max(0, value));
            }

            return spectrumData;
        }
        // 视频渲染器不应该实时计算频谱数据
        // 如果没有预计算的频谱数据，返回默认数据
        else {
            // 如果没有预计算的频谱数据，返回默认数据
            float[] spectrumData = new float[bandCount];
            for (int i = 0; i < bandCount; i++) {
                spectrumData[i] = 0.2f; // 进一步提高默认能量值
            }
            return spectrumData;
        }
    }
}